﻿using System;
using System.IO;
using Dokan;
using System.Collections;
using System.Collections.Generic;


namespace WebLearningFS
{
	public class WLFS : DokanOperations
	{

		private WebQuest wqClass, wqSpace;

		private string courseName = @"\Storage Technology";
		private string spaceName = @"\MySpace";
		private Dictionary<string, int> filenameDict = new Dictionary<string, int>();
		private Dictionary<string, byte[]> filestreamDict = new Dictionary<string, byte[]>();
		private Dictionary<string, MemoryStream> spaceDict = new Dictionary<string, MemoryStream>();

		private string root_;
		private int count_;
		private FileStream x = new FileStream("D:\\a.pdf", FileMode.Create);
		private int cnt = 0;

        public WLFS(WebQuest wqa, WebQuest wqb)
        {
			wqClass = wqa;
			wqClass.Login();
			wqSpace = wqb;
			wqb.Login();
			root_ = @"D:\Test";
            count_ = 1;
        }

        private string GetPath(string filename)
        {
            string path = root_ + filename;
            Console.Error.WriteLine("GetPath : {0}", path);
            return path;
        }

		private string CutName(string filename)
		{
			string name = filename.Substring(filename.LastIndexOf('\\') + 1);
			return name;
		}

        public int CreateFile(String filename, FileAccess access, FileShare share,
            FileMode mode, FileOptions options, DokanFileInfo info)
        {
			Console.WriteLine("I am in CreateFile and is creating: " + filename);
            string path = GetPath(filename);
            info.Context = count_++;

			if (filename.StartsWith(courseName))
			{
				if (filename.Length == courseName.Length)
				{
					info.IsDirectory = true;
					return 0;
				}
				else
				{
					string name = CutName(filename);
					if (wqClass.FileLink.ContainsKey(name))
					{
						Console.WriteLine("Success");
						return 0;
					}
					else
					{
						Console.WriteLine("Failure");
						return -DokanNet.ERROR_FILE_NOT_FOUND;
					}
				}
			}
			if (filename.StartsWith(spaceName))
			{
				if (filename.Length == spaceName.Length)
				{
					info.IsDirectory = true;
					return 0;
				}
				else
				{
					string name = CutName(filename);
					if (wqSpace.FileLink.ContainsKey(name))
					{
						Console.WriteLine("Create_Operation succeeded");
						return 0;
					}
					else
					{
						Console.WriteLine("Create_Operation failed");
						return -DokanNet.ERROR_FILE_NOT_FOUND;
					}
				}
			}
			return -DokanNet.ERROR_FILE_NOT_FOUND;
		}

        public int OpenDirectory(String filename, DokanFileInfo info)
        {
			Console.WriteLine("In opening a dir " + filename);
            info.Context = count_++;

			if (filename.Equals(courseName) || filename.Equals(spaceName) || filename.Equals(@"\"))
				return 0;
			return -DokanNet.ERROR_PATH_NOT_FOUND;
        }



        public int CreateDirectory(String filename, DokanFileInfo info)
        {
			Console.WriteLine("I am in CreateDir :" + filename);
            return -1;
        }

        public int Cleanup(String filename, DokanFileInfo info)
        {
			Console.WriteLine("I am in Cleanup : " + filename);
            //Console.WriteLine("%%%%%% count = {0}", info.Context);
			int status;
			if (filenameDict.TryGetValue(filename, out status) && status == 3)
			{
				filenameDict[filename] = 0;
				byte[] buffer = spaceDict[filename].GetBuffer();
				wqSpace.UploadFile(buffer, CutName(filename));
			}
            return 0;
        }

        public int CloseFile(String filename, DokanFileInfo info)
        {
			Console.WriteLine("I am in closefile");
            return 0;
        }

        public int ReadFile(String filename, Byte[] buffer, ref uint readBytes,
            long offset, DokanFileInfo info)
        {			
			int status;
			if (!filenameDict.TryGetValue(filename, out status))
				status = 0;
			if (status == 0)
			{
				filenameDict[filename] = 1;
				string name = CutName(filename);
				++cnt;
				if (filename.StartsWith(courseName))
					filestreamDict[filename] = wqClass.Download(wqClass.FileLink[name]);
				else if (filename.StartsWith(spaceName))
				{
					filestreamDict[filename] = wqSpace.Download(wqSpace.FileLink[name]);
				}
				filenameDict[filename] = 2;
			}
			if (filenameDict[filename] == 2)
			{
				try
				{
					MemoryStream ms = new MemoryStream(filestreamDict[filename]);
					ms.Seek(offset, SeekOrigin.Begin);
					readBytes = (uint) ms.Read(buffer, 0, buffer.Length);
					return 0;
				}
				catch (Exception)
				{
					return -1;
				}
			}
			return -1;
        }

        public int WriteFile(String filename, Byte[] buffer,
            ref uint writtenBytes, long offset, DokanFileInfo info)
        {
			int status;
			if (!filenameDict.TryGetValue(filename, out status))
				status = 0;
			if (status != 3)
			{
				spaceDict[filename] = new MemoryStream();
			}
			filenameDict[filename] = 3;
			try
			{
				spaceDict[filename].Seek(offset, SeekOrigin.Begin);
				//It is a trivial thing to do.
				spaceDict[filename].Write(buffer, 0, buffer.Length);
				writtenBytes = (uint)buffer.Length;
				return 0;
			}
			catch (Exception)
			{
				return -1;
			}
        }

        public int FlushFileBuffers(String filename, DokanFileInfo info)
        {
			Console.WriteLine("I am in FlushFileBuffers : " + filename);
            return -1;
        }

        public int GetFileInformation(String filename, FileInformation fileinfo, DokanFileInfo info)
        {
			Console.WriteLine("In getfileinform");
            string path = GetPath(filename);

			if (filename.StartsWith(courseName))
			{
				if (filename.Length == courseName.Length)
				{
					fileinfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Directory;
					fileinfo.CreationTime = DateTime.UtcNow;
					fileinfo.LastAccessTime = DateTime.UtcNow;
					fileinfo.LastWriteTime = DateTime.UtcNow;
					fileinfo.Length = 0;
					return 0;
				}
				else
				{
					string name = CutName(filename);
					if (wqClass.FileLink.ContainsKey(name))
					{
						foreach (var item in wqClass.ClassMaterials)
						{
							if (item.Meta == name)
							{
								fileinfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Archive;
								DateTime dt;
								DateTime.TryParse(item.Date, out dt);
								fileinfo.CreationTime = dt;
								fileinfo.LastAccessTime = dt;
								fileinfo.LastWriteTime = dt;
								fileinfo.Length = long.Parse(item.Size);
								return 0;
							}
						}
					}
				}
			}

			if (filename.StartsWith(spaceName))
			{
				if (filename.Length == spaceName.Length)
				{
					fileinfo.Attributes = FileAttributes.Directory;
					fileinfo.CreationTime = DateTime.UtcNow;
					fileinfo.LastAccessTime = DateTime.UtcNow;
					fileinfo.LastWriteTime = DateTime.UtcNow;
					fileinfo.Length = 0;
					return 0;
				}
				else
				{
					string name = CutName(filename);
					if (wqSpace.FileLink.ContainsKey(name))
					{
						foreach (var item in wqSpace.SpaceMaterials)
						{
							if (item.Head == name)
							{
								fileinfo.Attributes = FileAttributes.Archive;
								fileinfo.CreationTime = DateTime.UtcNow;
								fileinfo.LastAccessTime = DateTime.UtcNow;
								fileinfo.LastWriteTime = DateTime.UtcNow;
								fileinfo.Length = long.Parse(item.Size);
								return 0;
							}
						}
					}
				}
			}
			return -1;
        }

		public int FindFiles(String filename, ArrayList files, DokanFileInfo info)
		{
			Console.WriteLine("Findfiles");

			if (filename.Equals(@"\"))
			{
				//Section A: Class Materials
				FileInformation fi = new FileInformation();
				fi.Attributes = FileAttributes.ReadOnly | FileAttributes.Directory;
				fi.CreationTime = DateTime.UtcNow;
				fi.LastAccessTime = DateTime.UtcNow;
				fi.LastWriteTime = DateTime.UtcNow;
				fi.Length = 0;
				fi.FileName = courseName.Substring(1);
				files.Add(fi);

				//Section B: MySpace
				fi = new FileInformation();
				fi.Attributes = FileAttributes.Directory;
				fi.CreationTime = DateTime.UtcNow;
				fi.LastAccessTime = DateTime.UtcNow;
				fi.LastWriteTime = DateTime.UtcNow;
				fi.Length = 0;
				fi.FileName = spaceName.Substring(1);
				files.Add(fi);

				return 0;
			}

			if (filename.StartsWith(courseName))
			{
				foreach (var item in wqClass.ClassMaterials)
				{
					FileInformation fi = new FileInformation();
					fi.Attributes = FileAttributes.ReadOnly | FileAttributes.Archive;
					DateTime dt;
					DateTime.TryParse(item.Date, out dt);
					fi.CreationTime = dt;
					fi.LastAccessTime = dt;
					fi.LastWriteTime = dt;
					fi.Length = long.Parse(item.Size);
					fi.FileName = item.Meta;
					files.Add(fi);
				}
				return 0;
			}
			if (filename.StartsWith(spaceName))
			{
				foreach (var item in wqSpace.SpaceMaterials)
				{
					FileInformation fi = new FileInformation();
					fi.Attributes = FileAttributes.Archive;
					fi.CreationTime = DateTime.UtcNow;
					fi.LastAccessTime = DateTime.UtcNow;
					fi.LastWriteTime = DateTime.UtcNow;
					fi.Length = long.Parse(item.Size);
					fi.FileName = item.Head;
					files.Add(fi);
				}
				return 0;
			}
			return -1;
		}

		public int SetFileAttributes(String filename, FileAttributes attr, DokanFileInfo info)
		{
			Console.WriteLine("In SetFileAttributes");
			return -1;
		}

		public int SetFileTime(String filename, DateTime ctime,
				DateTime atime, DateTime mtime, DokanFileInfo info)
		{
			Console.WriteLine("In SetFileTime");
			return -1;
		}

		public int DeleteFile(String filename, DokanFileInfo info)
		{
			if (filename.StartsWith(spaceName))
			{

			}
			return -1;
		}

		public int DeleteDirectory(String filename, DokanFileInfo info)
		{
			Console.WriteLine("In DeleteDirectory");
			return -1;
		}

		public int MoveFile(String filename, String newname, bool replace, DokanFileInfo info)
		{
			Console.WriteLine("In MoveFile");
			return -1;
		}

		public int SetEndOfFile(String filename, long length, DokanFileInfo info)
		{
			Console.WriteLine("In SetEndOfFile");
			return -1;
		}

		public int SetAllocationSize(String filename, long length, DokanFileInfo info)
		{
			Console.WriteLine("In SetAllocationSize");
			return -1;
		}

		public int LockFile(String filename, long offset, long length, DokanFileInfo info)
		{
			Console.WriteLine("In LockFile");
			return 0;
		}

		public int UnlockFile(String filename, long offset, long length, DokanFileInfo info)
		{
			Console.WriteLine("In UnlockFile");
			return 0;
		}

		public int GetDiskFreeSpace(ref ulong freeBytesAvailable, ref ulong totalBytes,
			ref ulong totalFreeBytes, DokanFileInfo info)
		{
			Console.WriteLine("In GetDiskFreeSpace");
			totalBytes = 1024UL * 1024 * 1024 * 1024 ;
			totalFreeBytes = totalBytes / 3 * 2;
			freeBytesAvailable = totalFreeBytes;
			return 0;
		}

		public int Unmount(DokanFileInfo info)
		{
			Console.WriteLine("In Unmount");
			return 0;
		}

		static void Main(string[] args)
		{
			Console.Write("Input your account name and password.\nUsername:");
			string name = Console.ReadLine();
			Console.Write("Password:");
			//An lazy way to mask the password input
			ConsoleColor oldFore = Console.ForegroundColor;
			Console.ForegroundColor = Console.BackgroundColor;
			string password = Console.ReadLine();
			Console.ForegroundColor = oldFore;
			DokanOptions opt = new DokanOptions();
			opt.DebugMode = true;
			opt.MountPoint = "P:\\";
			opt.ThreadCount = 1;
			opt.RemovableDrive = true;
			opt.VolumeLabel = "Eddie's HardDrive";
			int status = DokanNet.DokanMain(opt, new WLFS(new WebQuest(name, password, 1), new WebQuest(name, password, 2)));
			switch (status)
			{
				case DokanNet.DOKAN_DRIVE_LETTER_ERROR:
					Console.WriteLine("Driver letter error");
					break;
				case DokanNet.DOKAN_DRIVER_INSTALL_ERROR:
					Console.WriteLine("Driver install error");
					break;
				case DokanNet.DOKAN_MOUNT_ERROR:
					Console.WriteLine("Mount error");
					break;
				case DokanNet.DOKAN_START_ERROR:
					Console.WriteLine("Start error");
					break;
				case DokanNet.DOKAN_ERROR:
					Console.WriteLine("Unknown error");
					break;
				case DokanNet.DOKAN_SUCCESS:
					Console.WriteLine("Success");
					break;
				default:
					Console.WriteLine("Unknown status: %d", status);
					break;
			}
		}
	}
}
